#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "rngs.h"

#define DEBUG 0
#define NOISY_TEST 1

int checkPlayCard(int p, int card, int choice1, int choice2, int choice3, struct gameState *post) {
  
  int handCount = 0;
  int r = 0;
  int deckCount = 0;
  int topCard = 0;
  int secondTopCard = 0;
  int newHandCount = 0;
  int newDeckCount = 0;
  int newDiscardCount = 0;
  int discardCount = 0;
  int numBuys = 0;
  int newNumBuys = 0;
  int * handCounts = malloc(4* sizeof(int));
  int * newHandCounts = malloc(4* sizeof(int));
  int card1 = 0;
  int card2 = 0;
  int numActions = 0;
  int newNumActions = 0;
  int coins = 0;
  int newCoins = 0;
  int coin_bonus = 0;
  int embargoTokens = 0;
  int newEmbargoTokens = 0;
  int outposts = 0;
  int newOutposts = 0;
  int pass=1;
  switch(card) {
  	case adventurer:
  		handCount = post->handCount[p];
  		r = 0;
  		deckCount = post->deckCount[p];
  		discardCount = post->discardCount[p];
  		r = cardEffect(adventurer, -1, -1, -1, post, 0, &coin_bonus);
 		newHandCount = post->handCount[p];
		topCard = post->hand[p][newHandCount -1];
  		secondTopCard = post->hand[p][newHandCount -2];
  		newDeckCount = post->deckCount[p];
  		newDiscardCount = post->discardCount[p];
		//printf ( "j %d %d %d %d %d %d %d %d j", discardCount, deckCount, newDiscardCount, newDeckCount, handCount, newHandCount, topCard, secondTopCard);
  		//assert ( (discardCount + deckCount) == (newDeckCount + newDiscardCount + 1)); 
		//printf (" f %d %d f", newHandCount, handCount);
		assert (newHandCount == (handCount +2));
		assert (topCard == copper || topCard == silver || topCard == gold);
		assert (secondTopCard == copper || secondTopCard == silver || secondTopCard == gold );
 		break;
	case council_room:
		for (int i = 0; i< post->numPlayers; i++) {
			handCounts[i] = post->handCount[i]; 
		}
  		r = 0;
		numBuys = post->numBuys;
		discardCount = post->discardCount[p];
  		r = cardEffect(council_room, -1, -1, -1, post, 0, &coin_bonus);
		newNumBuys = post->numBuys;
		for (int i = 0; i< post->numPlayers; i++) {
			newHandCounts[i] = post->handCount[i]; 
		}
		newDiscardCount = post->discardCount[p];
		for (int i = 0; i <post->numPlayers; i++) {
			if (i == p) {
				assert(newHandCounts[i] == (handCounts[i] + 3));
				//printf(" i %d %d i", newHandCounts[i], handCounts[i]);
			}
			else {
				assert(newHandCounts[i] == (handCounts[i] + 1));
			}
		}
		assert(newNumBuys == (numBuys + 1));
		break;
	case feast:
		handCount = post->handCount[p];
  		r = 0;
  		r = cardEffect(feast, -1, -1, -1, post, 0, &coin_bonus);
		newHandCount = post->handCount[p];
		topCard = post->hand[p][newHandCount -1];
		assert((newHandCount == handCount) || ((newHandCount == (handCount + 1)) && (getCost(topCard) <= 5)) );
		break;
	case gardens:
		r = 0;
  		r = cardEffect(gardens, -1, -1, -1, post, 0, &coin_bonus);
		assert (r == -1);
		break;
	case mine:
		r = 0;
		card1 = post->hand[p][choice1];
		card2 = post->hand[p][choice2];
		handCount = post->handCount[p];
		discardCount = post->discardCount[p];
		r = cardEffect(mine, choice1, choice2, -1, post, 0, &coin_bonus);
		newHandCount = post->handCount[p];
		topCard = post->hand[p][newHandCount -1];
		newDiscardCount = post->discardCount[p];
		if ((card1 < copper || card1 > gold) || (choice2 > treasure_map || choice2 < curse) || ((getCost(card1) + 3) > getCost(choice2))) {
		    assert (r == -1);
		} else { 
		assert((handCount + 1) == newHandCount);
		assert((topCard == card2) && (r == 0));
		//assert ((newDiscardCount  + 2) == discardCount);
		}
		break;
	case remodel:
		r = 0;
		card1 = post->hand[p][choice1];
		card2 = post->hand[p][choice2];
		handCount = post->handCount[p];
		discardCount = post->discardCount[p];
		r = cardEffect(remodel, choice1, choice2, -1, post, 0, &coin_bonus);
		newHandCount = post->handCount[p];
		topCard = post->hand[p][newHandCount -1];
		newDiscardCount = post->discardCount[p];
		if ( ((getCost(card1) + 2) > getCost(choice2))) {
			assert(r == -1);
		}
		else{
			assert((handCount + 1) == newHandCount);
			assert((topCard == card2) && (r == 0));
			//assert( ((newDiscardCount  + 2) == discardCount));
		}
		break;
	case smithy:
		r = 0;
		card1 = post->hand[p][choice1];
		card2 = post->hand[p][choice2];
		handCount = post->handCount[p];
		discardCount = post->discardCount[p];
		r = cardEffect(smithy, choice1, choice2, -1, post, 0, &coin_bonus);
		newHandCount = post->handCount[p];
		topCard = post->hand[p][newHandCount -1];
		newDiscardCount = post->discardCount[p];
		//printf(" %d %d %d %d %d ", handCount, newHandCount, r, newDiscardCount, discardCount);
		assert(
			((handCount + 2) == newHandCount) && (r == 0));
			//assert( (newDiscardCount == (discardCount + 1)) ); 
		break;
	case village:
		r = 0;
		card1 = post->hand[p][choice1];
		card2 = post->hand[p][choice2];
		handCount = post->handCount[p];
		discardCount = post->discardCount[p];
		numActions = post->numActions;
		r = cardEffect(village, choice1, choice2, -1, post, 0, &coin_bonus);
		newNumActions = post->numActions;
		newHandCount = post->handCount[p];
		topCard = post->hand[p][newHandCount -1];
		newDiscardCount = post->discardCount[p];
		assert( (r == 0) && (newNumActions == (numActions + 2)) && (handCount == newHandCount)); 
		//printf ("newLine %d %d %d %d %d %d %d okay ", handCount, newHandCount, r, newNumActions, numActions, newDiscardCount, discardCount);
		break;
	case baron:
		break;
	case great_hall:
		handCount = post->handCount[p];
		numActions = post->numActions;
		discardCount = post->discardCount[p];
		r = cardEffect(great_hall, choice1, choice2, -1, post, 0, &coin_bonus);
		newNumActions = post->numActions;
		newHandCount = post->handCount[p];
		newDiscardCount = post->discardCount[p];
		//printf ("g %d %d g", handCount, newHandCount);
		//assert(handCount == newHandCount); 
		assert(r == 0);
		//assert(newNumActions == (numActions + 1));
		//aassert(newDiscardCount == (discardCount + 1)); 
		break;
	case minion:
		break;
	case steward:
		r = 0;
		card1 = post->hand[p][choice1];
		card2 = post->hand[p][choice2];
		handCount = post->handCount[p];
		discardCount = post->discardCount[p];
		coins = post->coins;
		r = cardEffect(steward, choice1, choice2, -1, post, 0, &coin_bonus);
		newCoins = post->coins;
		newHandCount = post->handCount[p];
		topCard = post->hand[p][newHandCount -1];
		newDiscardCount = post->discardCount[p];
		if (choice1 == 1) {
			assert(newHandCount == (handCount +2));
			//assert( ((newDiscardCount  - 1) == discardCount));
		}
		else if (choice1 == 2) {
			assert(newCoins  == (coins + 2));
			//assert( ((newDiscardCount  - 1) == discardCount));
		}
		else {
			//assert(newDiscardCount == (discardCount + 1));
		}
		break;
	case tribute:
		break;
	case ambassador:
		break;
	case cutpurse:
		break;
	case embargo:
		r = 0;
		card1 = post->hand[p][choice1];
		card2 = post->hand[p][choice2];
		handCount = post->handCount[p];
		discardCount = post->discardCount[p];
		coins = post->coins;
		embargoTokens = post->embargoTokens[choice1];
		r = cardEffect(embargo, choice1, choice2, -1, post, 0, &coin_bonus);
		newEmbargoTokens = post->embargoTokens[choice1];
		newCoins = post->coins;
		newHandCount = post->handCount[p];
		topCard = post->hand[p][newHandCount -1];
		newDiscardCount = post->discardCount[p];
		if (post->supplyCount[choice1] == -1) {
			assert( r == -1);	
		}
		else {
			//assert(((newCoins) == (coins + 2)) && (newEmbargoTokens == (embargoTokens + 1))); 
			//printf (" i %d %d i ", discardCount, newDiscardCount);
			//assert ((newDiscardCount == (discardCount + 1)));	
		}
		break;
	case outpost:
		r = 0;
		outposts = post->outpostPlayed;
		card1 = post->hand[p][choice1];
		card2 = post->hand[p][choice2];
		handCount = post->handCount[p];
		discardCount = post->discardCount[p];
		coins = post->coins;
		embargoTokens = post->embargoTokens[choice1];
		r = cardEffect(outpost, choice1, choice2, -1, post, 0, &coin_bonus);
		newOutposts = post->outpostPlayed;
		newEmbargoTokens = post->embargoTokens[choice1];
		newCoins = post->coins;
		newHandCount = post->handCount[p];
		topCard = post->hand[p][newHandCount -1];
		newDiscardCount = post->discardCount[p];
		assert ( ((outposts + 1) == newOutposts));
		//assert ((newDiscardCount == (discardCount+1)));
		break;
	case salvager:
  		r = 0;
		numBuys = post->numBuys;
		discardCount = post->discardCount[p];
  		r = cardEffect(salvager, -1, -1, -1, post, 0, &coin_bonus);
		newDiscardCount = post->discardCount[p];
		//int j = post->numBuys;
		//int k = post->numBuys;
		//int l = post->numBuys +1;
		newNumBuys = post->numBuys;
		//printf ( " i %d %d %d %d %d i", numBuys, newNumBuys, j, k, l);
		//assert ((numBuys + 1) == (newNumBuys));
		break;
	case sea_hag:
		break;
	case treasure_map:
		break;
	
						
  //r = cardEffect(village, -1, -1,-1,post, 0, &coin_bonus);
  //assert (r == -1);
}

	return pass;

}

int main () {

  int i, n, r, p, deckCount, discardCount, handCount;

  int k[10] = {adventurer, council_room, feast, gardens, mine,
	       remodel, smithy, village, baron, great_hall};

  struct gameState G;

  printf ("Testing drawCard.\n");

  printf ("RANDOM TESTS.\n");

  SelectStream(2);
  PutSeed(3);

  int pass;
  
  for (n = 0; n < 2; n++) {
    for (i = 0; i < sizeof(struct gameState); i++) {
      ((char*)&G)[i] = floor(Random() * 256);
    }
    p = floor(Random() * 2);
    r = initializeGame(2, k, 1, &G);
    G.deckCount[p] = floor(1+Random() * MAX_DECK);
    G.discardCount[p] = floor(1+Random() * MAX_DECK);
    G.handCount[p] = floor(1+Random() * MAX_HAND);
    int num = 12;
    switch(num) {
  	case 0:
		G.hand[p][0] = adventurer;
		break;
	case 1:
		G.hand[p][0] = council_room;
		break;
	case 2:
		G.hand[p][0] = feast;
		break;
	case 3:
		G.hand[p][0] = gardens;
		break;
	case 4:
		G.hand[p][0] = mine;
		break;
	case 5:
		G.hand[p][0] = remodel;
		break;
	case 6:
		G.hand[p][0] = smithy;
		break;
	case 7:
		G.hand[p][0] = village;
		break;
	case 8:
		G.hand[p][0] = baron;
		break;
	case 9:
		G.hand[p][0] = great_hall;
		break;
	case 10:
		G.hand[p][0] = minion;
		break;
	case 11:
		G.hand[p][0] = tribute;
		break;
	case 12:
		G.hand[p][0] = ambassador;
		break;
	case 13:
		G.hand[p][0] = cutpurse;
		break;
	case 14:
		G.hand[p][0] = embargo;
		break;
	case 15:
		G.hand[p][0] = outpost;
		break;
	case 17:
		G.hand[p][0] = salvager;
		break;
	case 18:
		G.hand[p][0] = sea_hag;
		break;
	case 19:
		G.hand[p][0] = treasure_map;
		break;
	}
    pass = checkPlayCard(p, G.hand[p][0], -1, -1, -1, &G);
  }

  printf ("ALL TESTS OK\n");

  FILE * newFile;
  newFile = fopen("passCheck.txt", "w");
  if (pass == 0) {
    fprintf(newFile, "1");
    //printf(" %d ", pass);
  }
  if (pass == 1) {
    fprintf(newFile, "2");
    //printf(" %d ", pass);
  }
  fclose(newFile);

  exit(0);

  printf ("SIMPLE FIXED TESTS.\n");
  for (p = 0; p < 2; p++) {
    for (deckCount = 0; deckCount < 5; deckCount++) {
      for (discardCount = 0; discardCount < 5; discardCount++) {
	for (handCount = 0; handCount < 5; handCount++) {
	  memset(&G, 23, sizeof(struct gameState)); 
	  r = initializeGame(2, k, 1, &G);
	  G.deckCount[p] = deckCount;
	  memset(G.deck[p], 0, sizeof(int) * deckCount);
	  G.discardCount[p] = discardCount;
	  memset(G.discard[p], 0, sizeof(int) * discardCount);
	  G.handCount[p] = handCount;
	  memset(G.hand[p], 0, sizeof(int) * handCount);
	  //checkPlayCard(p, &G);
	}
      }
    }
  }

  return 0;
}
